package com.eraasoftware.npa.server;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;

import com.eraasoftware.npa.io.ChannelBuffer;
import com.esotericsoftware.minlog.Log;

public class ServiceListener implements Runnable {

	ChannelBuffer channelBuffer;

	boolean valid = true;
	boolean busy = false;
	boolean thread = true;
	static int BUFFER_SIZE = 3000;

	public ServiceListener() throws IOException {
		this(null);
	}

	public void setClientChannel(SocketChannel clientChannel) {
		this.channelBuffer = new ChannelBuffer(clientChannel, BUFFER_SIZE);
	}

	public ServiceListener(SocketChannel clientChannel) throws IOException {
		setClientChannel(clientChannel);

	}

	ByteArrayOutputStream bao = new ByteArrayOutputStream();
	WritableByteChannel baoChannel = Channels.newChannel(bao);

	static byte[] ping;

	public boolean pingClient() {
		try {
			if (ping != null) {
				channelBuffer.clear();
				channelBuffer.write(ping);
				channelBuffer.send();
				channelBuffer.read();
				return true;
			} else {
				return true;
			}
		} catch (Exception e) {
			System.out.println("PingClient: FAIL " + e.getMessage());
			e.printStackTrace();
			return false;
		}

	}

	@Override
	public void run() {
		try {
			while (true) {
				Thread.sleep(1000);
				if (master) {
					checkMasterClient();
					System.out.println("Services available: " + ServiceListenerPoolFactory.instance().availableServices());
				} else if (!thread) {
					break;
				} else if (!busy) {
					ServiceListener service = ServiceListenerPoolFactory.instance().getService();
					if (!service.pingClient()) {
						ServiceListenerPoolFactory.instance().destroy(service);
						break;
					} else {
						ServiceListenerPoolFactory.instance().returnObject(service);
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	private void checkMasterClient() {
		int needClients = ServiceListenerPoolFactory.instance().needClients();
		Log.info("checkMasterClient " + needClients);
		if (needClients > 0) {
			try {
				createNewClient(needClients);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	private void createNewClient(int nbClients) throws IOException {
		Log.info("createNewClient " + nbClients);
		ByteBuffer newClients = ServiceListenerPoolFactory.getSettings().getCreateNewClientsBytes(nbClients);
		byte[] res = sendQuestion(newClients);

	}

	private boolean master = false;

	public byte[] readBytes() {
		bao.reset();

		while (channelBuffer.readable()) {
			try {
				if (channelBuffer.read() > 0) {
					baoChannel.write(channelBuffer.buffer());
				} else {
					System.out.println("read null");
					break;
				}

			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				break;
			}
		}
		return bao.toByteArray();
	}

	public byte[] sendQuestion(String question) throws IOException {

		// System.out.println("The question " + question);

		return sendQuestion(ByteBuffer.wrap(question.getBytes()));

	}

	public byte[] sendQuestion(ByteBuffer bb) throws IOException {
		setBusy(true);
		channelBuffer.clear();
		try {
			channelBuffer.write(bb);
			channelBuffer.send();
		} catch (IOException e) {
			valid(false);
			e.printStackTrace();
			throw e;
		}

		// System.out.println("The question ");
		byte[] res = this.readBytes();
		setBusy(false);
		return res;
	}

	public static void setPing(ByteBuffer pingBuffer) {

		ServiceListener.ping = new byte[pingBuffer.limit()];
		pingBuffer.get(ServiceListener.ping);
	}

	public boolean valid() {
		return valid;
	}

	public void valid(boolean corrupted) {
		this.valid = corrupted;
	}

	public void setBusy(boolean busy) {
		this.busy = busy;
	}

	public boolean isBusy() {
		return busy;
	}

	public String getName() {
		if (master) {
			return "ServiceListener* " + this.channelBuffer.channel();
		} else
			return "ServiceListener " + this.channelBuffer.channel();

	}

	public void master(boolean b) {
		this.master = b;
		this.busy = true;
	}

	public ServiceListener thread(boolean isThread) {
		this.thread = isThread;
		return this;
	}
}
